from typing import Optional
from uuid import UUID

from passlib.context import CryptContext

from fastapi_template.models import User

from .dto import UserCreateDTO, UserDTO, UserUpdateDTO
from .facade import UserFacade


class UserFacadeImpl(UserFacade):
    """Implementation of the user domain facade."""

    def __init__(self):
        self.pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

    def _hash_password(self, password: str) -> str:
        """Hash the password using bcrypt."""
        return self.pwd_context.hash(password)

    def _verify_password(self, plain_password: str,
                         hashed_password: str) -> bool:
        """Verify the password against its hash."""
        return self.pwd_context.verify(plain_password, hashed_password)

    async def create_user(self, user_data: UserCreateDTO) -> UserDTO:
        """Create a new user."""
        user = await User.create(username=user_data.username,
                                 email=user_data.email,
                                 hashed_password=self._hash_password(
                                     user_data.password),
                                 is_active=user_data.is_active,
                                 is_superuser=user_data.is_superuser)
        return UserDTO.model_validate(user)

    async def get_user_by_id(self, user_id: UUID) -> Optional[UserDTO]:
        """Get user by ID."""
        user = await User.get_or_none(id=user_id)
        return UserDTO.model_validate(user) if user else None

    async def get_user_by_username(self, username: str) -> Optional[UserDTO]:
        """Get user by username."""
        user = await User.get_or_none(username=username)
        return UserDTO.model_validate(user) if user else None

    async def get_user_by_email(self, email: str) -> Optional[UserDTO]:
        """Get user by email."""
        user = await User.get_or_none(email=email)
        return UserDTO.model_validate(user) if user else None

    async def update_user(self, user_id: UUID,
                          user_data: UserUpdateDTO) -> Optional[UserDTO]:
        """Update user information."""
        user = await User.get_or_none(id=user_id)
        if not user:
            return None

        update_data = user_data.model_dump(exclude_unset=True)
        if update_data:
            await user.update_from_dict(update_data).save()

        return UserDTO.model_validate(user)

    async def delete_user(self, user_id: UUID) -> bool:
        """Delete user by ID."""
        user = await User.get_or_none(id=user_id)
        if not user:
            return False
        await user.delete()
        return True

    async def verify_credentials(self, username: str,
                                 password: str) -> Optional[UserDTO]:
        """Verify user credentials."""
        user = await User.get_or_none(username=username)
        if not user or not self._verify_password(password,
                                                 user.hashed_password):
            return None
        return UserDTO.model_validate(user)
